/*
 * file: WG26驱动程序
 * author: yuyoufei86@qq.com
 * history:
 * 		V1.0:
 *			建立驱动,发现其和dht11很相似。参考了dht11的写法
 *			使用iio的框架
 *      V1.1: 2019.11.2
 *           修复pid hid错误 ,上一版本忘记进行位移操作。
 *      V1.2: 2019.11.9
            开始读加入定时器，超时返回超时错误 
 *           
 */

#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/iio/iio.h>
#include <linux/timer.h>

//每次刷卡D0 和D1共有26次下降沿
#define WG26_FALLING_EDGE 26
#define WG26_CARD_EDGE 4

#define CARD_FLAG '2'
#define BUTTON_FLAG '1'

#define DRIVER_NAME "wg26"
static const char *wg26_gpio_name[] = {"wg26_data0", "wg26_data1"};

struct wg26
{
	struct device *dev;
	int gpio[2];
	int irq[2];

	struct completion completion;

	u8 data[26];
	u8 cnt;

	u8 hid;
	u16 pid;
	u8 button_val;

	s64 timestamp;
	struct timer_list timer;
};

//给定时器中断用的全局变量，没有的话很难处理
static const struct of_device_id wg26_dt_ids[] = {
	{
		.compatible = "wg26",
	},
	{}};
MODULE_DEVICE_TABLE(of, wg26_dt_ids);

static const struct iio_chan_spec wg26_chan_spec[] = {
	{
		.type = IIO_TIMESTAMP,
		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
	},
};

#if 0
static char bValue[] = {'0', '1', '2', '3', '4', '5', '6',
						'7', '8', '9', 'S', 'N'};
#endif
//----------------------------------
//  0  1   2   4  .   .   .   24  25
//  E							  O
int wg26_check(struct wg26 *wg26)
{
	int i = 0;
	unsigned char ecc1 = 0;
	unsigned char ecc2 = 0;
	wg26->hid = 0;
	wg26->pid = 0;

	// for (i = 0; i < 26; i++)
	// {
		// printk("data%d: %d\n", i, wg26->data[i]);
	// }
	for (i = 0; i < 12; i++)
	{
		if (wg26->data[1 + i])
			ecc1++;
	}
	for (i = 0; i < 12; i++)
	{
		if (wg26->data[13 + i])
			ecc2++;
	}
	if ((ecc1 + wg26->data[0]) & 0x01)
	{
		printk("even check error\n");
		return -EIO;
	}
	if (!((ecc2 + wg26->data[25]) & 0x01))
	{
		printk("odd check error\n");
		return -EIO;
	}
	printk("check pass!\n");
	//2017.11.2 fix hid pid value error
	for (i = 0; i < 8; i++)
	{
		wg26->hid <<= 1;
		if (wg26->data[1 + i])
			(wg26->hid)++;
	}
	for (i = 0; i < 16; i++)
	{
		wg26->pid <<= 1;
		if (wg26->data[9 + i])
			(wg26->pid)++;
	}
	return 0;
}
static void get_button_val(struct wg26 *wg26)
{
	int i;
	wg26->button_val = 0;

	for (i = 0; i < 4; i++)
	{
		wg26->button_val <<= 1;
		if (wg26->data[i])
			(wg26->button_val)++;
	}
}
//超时函数
static void wg_timer_handler(unsigned long data)
{
	struct wg26 *wg26 = (struct wg26 *)data;
	if (wg26->cnt <= 0)
		return;
	// if (g_cnt == WG26_CARD_EDGE)
	// {
		// printk("Button\n");
	// }
	// else if (g_cnt == WG26_FALLING_EDGE)
	// {
		// printk("card");
	// }
	// else
	// {
		// printk("error cnt:%d\n", g_cnt);
	// }
	complete(&wg26->completion);
}

int multi(int val, int num)
{
	int i, total;
	total = val;
	for (i = 0; i < num; ++i)
	{
		total *= val;
	}
	return total;
}
int change_format(int card)
{
	int i;
	int inte;
	int tt;
	int j;
	i=0;
	for (;;)
	{
		inte = multi(10, i);
		j = card / inte;
		if (j == 0)
			break;
		else
			++i;
	}

	tt = 2 * inte + card;
	return tt;
}


static int wg26_read_raw(struct iio_dev *iio_dev,
						 const struct iio_chan_spec *chan,
						 int *val, int *val2, long m)
{
	struct wg26 *wg26 = iio_priv(iio_dev);
	int ret;
	int card_no;
	printk("read wg26 raw\n");

	reinit_completion(&wg26->completion);

	wg26->cnt = 0;
	

	memset(&wg26->data[0], 0, sizeof(wg26->data));
	ret = wait_for_completion_killable(&wg26->completion);

	//	printk("wg26 rec count:%d\n", wg26->cnt);
	if (ret == 0)
	{
		if ((wg26->cnt != WG26_FALLING_EDGE) && (wg26->cnt != WG26_CARD_EDGE))
		{
			dev_err(&iio_dev->dev,
					"Only %d signal edges detected\n",
					wg26->cnt);
			ret = -ETIMEDOUT;
		}
	}
	if (ret < 0)
		goto err;

	if (wg26->cnt == WG26_FALLING_EDGE)
	{
		ret = wg26_check(wg26);
		if (ret != 0)
			goto err;
	}
	else
	{
		get_button_val(wg26);
	}
	// hid | pid
	ret = IIO_VAL_INT;
	if (wg26->cnt == WG26_FALLING_EDGE)
	{
		card_no = (((int)(wg26->hid)) << 16) | (wg26->pid);
		*val = change_format(card_no);
	}
	else
		*val = 100 + wg26->button_val;
//note:应用程序拿到数据后 hid = *val / 65536
//						  pid = *val % 65536
#if 0
	if (wg26->cnt == 26)
	{
		printk("card\n");
		printk("hid:%d\n", wg26->hid);
		printk("pid:%d\n", wg26->pid);
		printk("value:%d\n", *val);
	}
	else
	{
		printk("Button :%c", bValue[wg26->button_val]);
		printk("value:%d\n", *val);
	}
#endif
err:
	wg26->cnt = -1;
	return ret;
}
static const struct iio_info wg26_iio_info = {
	.driver_module = THIS_MODULE,
	.read_raw = wg26_read_raw,
};

/*
 * Data0 中断
 */
static irqreturn_t wg26_handle_irq0(int irq, void *data)
{
	struct iio_dev *iio = data;
	struct wg26 *wg26 = iio_priv(iio);

	//first interrupt, start a 70ms timer,
	if (wg26->cnt == 0)
		mod_timer(&wg26->timer, jiffies + msecs_to_jiffies(70));

  printk("0\n");
	if (wg26->cnt < WG26_FALLING_EDGE && wg26->cnt >= 0)
	{
		wg26->data[wg26->cnt++] = 0;
		// if (wg26->cnt >= WG26_FALLING_EDGE)
		// 	complete(&wg26->completion);
	}
	//printk("%d\n", wg26->cnt);
	return IRQ_HANDLED;
}

/*
 * Data1 中断
 */
static irqreturn_t wg26_handle_irq1(int irq, void *data)
{
	struct iio_dev *iio = data;
	struct wg26 *wg26 = iio_priv(iio);

	if (wg26->cnt == 0)
		mod_timer(&wg26->timer, jiffies + msecs_to_jiffies(70));
  printk("1\n");
	if (wg26->cnt < WG26_FALLING_EDGE && wg26->cnt >= 0)
	{
		wg26->data[wg26->cnt++] = 1;

		// if (wg26->cnt >= WG26_FALLING_EDGE)
		// 	complete(&wg26->completion);
	}
	//printk("%d\n", wg26->cnt);
	return IRQ_HANDLED;
}

static int wg26_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *node = dev->of_node;
	struct wg26 *wg26;
	struct iio_dev *iio;
	int ret;
	int i;
	iio = devm_iio_device_alloc(dev, sizeof(*wg26));
	if (!iio)
	{
		dev_err(dev, "failed to allocate IIO device\n");
		return -ENOMEM;
	}

	wg26 = iio_priv(iio);
	wg26->dev = dev;

	for (i = 0; i < 2; i++)
	{
		wg26->gpio[i] = ret = of_get_gpio(node, i);
		if (ret < 0)
		{
			dev_err(dev, "get gpio %d error\n", i);
			return ret;
		}

		ret = devm_gpio_request_one(dev, wg26->gpio[i], GPIOF_IN, wg26_gpio_name[i]);
		if (ret)
		{
			dev_err(dev, "couled not request gpio %d\n", i);
			return ret;
		}

		wg26->irq[i] = gpio_to_irq(wg26->gpio[i]);
		if (wg26->irq[i] < 0)
		{
			dev_err(dev, "GPIO %d has no interrupt\n", wg26->gpio[i]);
			return -EINVAL;
		}
		if (i == 0)
			ret = devm_request_irq(dev, wg26->irq[i], wg26_handle_irq0, IRQF_TRIGGER_FALLING, pdev->name, iio);
		else if (i == 1)
			ret = devm_request_irq(dev, wg26->irq[i], wg26_handle_irq1, IRQF_TRIGGER_FALLING, pdev->name, iio);
		if (ret)
		{
			dev_err(dev, "WG26_DATA%d request irq failed\n", i);
			return ret;
		}
		gpio_direction_input(wg26->gpio[i]);
	}

	 wg26->cnt = -1;
	// init timer
	init_timer(&wg26->timer);
	//timer.expires = jiffies + msecs_to_jiffies(70);
	wg26->timer.function = &wg_timer_handler;
	wg26->timer.data = (unsigned long )wg26;
	add_timer(&wg26->timer);

	platform_set_drvdata(pdev, iio);

	init_completion(&wg26->completion);

	iio->name = pdev->name;
	iio->dev.parent = &pdev->dev;
	iio->info = &wg26_iio_info;
	iio->modes = INDIO_DIRECT_MODE;
	iio->channels = wg26_chan_spec;
	iio->num_channels = ARRAY_SIZE(wg26_chan_spec);

	return devm_iio_device_register(dev, iio);
}

// static int wg26_remove(struct platform_device *pdev)
// {
// struct iio_dev *indio_dev = platform_get_drvdata(pdev);
// iio_device_unregister(indio_dev);
// return 0;
// }

static struct platform_driver wg26_driver = {
	.probe = wg26_probe,
	//.remove = wg26_remove,
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = wg26_dt_ids,
	},
};

module_platform_driver(wg26_driver);

MODULE_AUTHOR("Yu YouFei <yuyoufei86@qq.com>");
MODULE_DESCRIPTION("WG26  entrance guard");
MODULE_LICENSE("GPL v2");
